"""
Copyright (c) 2025-2025 Huawei Technologies Co., Ltd.

sysHAX is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
    http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
PURPOSE.
See the Mulan PSL v2 for more details.
Created: 2025-05-23
Desc:sysHAX 资源监控模块
"""

import re
from re import Pattern
import httpx

from src.utils.config import CPU_HOST, CPU_PORT, GPU_HOST, GPU_PORT
from src.utils.logger import Logger
from src.core.metrics import MetricsService

# Prometheus指标正则匹配模式
# 资源使用指标
RE_GPU_CACHE = re.compile(
    r"vllm:gpu_cache_usage_perc{[^}]*}\s+([\d.]+)",
)  # GPU KV缓存使用率：值域0-1，1表示100%使用
RE_CPU_CACHE = re.compile(
    r"vllm:cpu_cache_usage_perc{[^}]*}\s+([\d.]+)",
)  # CPU KV缓存使用率：值域0-1，1表示100%使用
RE_RUNNING_REQS = re.compile(
    r"vllm:num_requests_running{[^}]*}\s+(\d+)",
)  # 运行中请求数：当前在GPU上执行的请求数量
RE_WAITING_REQS = re.compile(
    r"vllm:num_requests_waiting{[^}]*}\s+(\d+)",
)  # 等待中请求数：等待GPU资源的请求数量
RE_SWAPPED_REQS = re.compile(
    r"vllm:num_requests_swapped{[^}]*}\s+(\d+)",
)  # 已交换请求数：从GPU交换到CPU内存的请求数量

class ResourceMonitor:
    """
    资源监控类，解析单个vLLM服务的Prometheus指标

    职责：
    1. 从指定URL获取单个服务的指标
    2. 解析指标并提供简单的接口访问这些指标
    """

    def __init__(self, metrics_url: str) -> None:
        """初始化资源监控器"""
        self.metrics_url = metrics_url

    def update_metrics(self) -> dict[str, float | int]:
        """
        更新指标，只在需要时获取
        """
        monitor_data = {
            "gpu_cache_usage": 0.0,   # GPU KV缓存使用率，百分比
            "cpu_cache_usage": 0.0,   # CPU KV缓存使用率，百分比
            "num_running": 0,         # 运行中请求数
            "num_waiting": 0,         # 等待中请求数
            "num_swapped": 0,         # 已交换请求数
        }
        try:
            # 发起HTTP请求获取指标
            with httpx.Client() as client:
                response = client.get(self.metrics_url, timeout=3.0)

                if response.status_code != httpx.codes.OK:
                    Logger.warning(f"获取指标失败: HTTP {response.status_code}")
                    return monitor_data

                monitor_text = response.text
                monitor_data["gpu_cache_usage"] = self._parse_metrics(monitor_text, RE_GPU_CACHE, float)
                monitor_data["cpu_cache_usage"] = self._parse_metrics(monitor_text, RE_CPU_CACHE, float)
                monitor_data["num_running"] = self._parse_metrics(monitor_text, RE_RUNNING_REQS, int)
                monitor_data["num_waiting"] = self._parse_metrics(monitor_text, RE_WAITING_REQS, int)
                monitor_data["num_swapped"] = self._parse_metrics(monitor_text, RE_SWAPPED_REQS, int)
                return monitor_data
        except httpx.TimeoutException as e:
            Logger.warning(f"获取指标超时: {e}")
            return monitor_data
        except httpx.HTTPStatusError as e:
            Logger.error(f"Monitor错误: {e}")
            return monitor_data
        except Exception as e:
            Logger.error(f"Monitor监控失败: {e}", exc_info=True)
            return monitor_data

    def _parse_metrics(self, metrics_text: str, pattern: Pattern, converter) -> None:
        match = pattern.search(metrics_text)
        if match:
            return converter(match.group(1))
        return converter("0")

class SystemMonitor:
    """
    系统监控类，同时监控GPU和CPU服务
    """

    def __init__(self, metrics_service: MetricsService) -> None:
        """初始化系统监控器：根据配置拼接 metrics URL"""
        # 构建 GPU/CPU metrics URL
        self.gpu_monitor = ResourceMonitor(f"http://{GPU_HOST}:{GPU_PORT}/metrics")
        self.cpu_monitor = ResourceMonitor(f"http://{CPU_HOST}:{CPU_PORT}/metrics")
        self.metrics_service = metrics_service

    def get_gpu_monitor(self) -> None:
        monitor_data = self.gpu_monitor.update_metrics()
        self.metrics_service.set_gpu_cache_usage(monitor_data["gpu_cache_usage"])


    def get_cpu_monitor(self) -> None:
        monitor_data = self.cpu_monitor.update_metrics()
        self.metrics_service.set_cpu_cache_usage(monitor_data["cpu_cache_usage"])
